package Schedulers;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import threadedSimulator.Cpu;
import Processes.Processes;

//The scheduler will be called each time the CPU receives a system timer 
//interrupt or a process makes a system call. It will determine what event 
//activated it and then determine which runnable process to execute next 
//(don't forget about the context switch). You should implement schedulers 
//		for the round-robin, priority, and lottery scheduling policies.

public class PriorityScheduler extends Scheduler {

	private static int TWO_CAP = 15;
	
	private static int THREE_CAP = 20;
	
	private int priority_counter_two = 0;
	
	private int priority_counter_three = 0;
	
	/**
	 * This PriorityScheduler's list of processes, organized by priority.
	 */
	private List<List<Processes>> my_list_of_processes;
	
	public PriorityScheduler(Cpu the_cpu) {
		super(the_cpu);
		my_list_of_processes = new LinkedList<List<Processes>>();
		for (int i = 0; i < 3; i++) {
			my_list_of_processes.add(new LinkedList<Processes>());
		}
	}

	@Override
	public void haltCurrentProcess() {
		Processes last = null;
		int highestPriority = getHighestPriority();
		last = my_list_of_processes.get(highestPriority).remove(0);
	
	    last.running = false;
	    if (!last.has_finished) {
			addProcess(last);
	    }
		my_cpu.addToOutput(toString(), "Scheduler");
			
		
		priority_counter_two++;
		priority_counter_three++;
		
		if (priority_counter_two % TWO_CAP == 0) {
			while (!my_list_of_processes.get(1).isEmpty()) {
				my_list_of_processes.get(0).add(my_list_of_processes.get(1).remove(0));
			}
		} else if (priority_counter_three % THREE_CAP == 0) {
			while (!my_list_of_processes.get(2).isEmpty()) {
				my_list_of_processes.get(0).add(my_list_of_processes.get(2).remove(0));
			}
		}
		
		highestPriority = getHighestPriority();
		my_list_of_processes.get(highestPriority).get(0).running = true;
		my_list_of_processes.get(highestPriority).get(0).run();
	}

	@Override
	public void addProcess(Processes the_process) {
		my_list_of_processes.get(the_process.my_priority - 1).add(the_process);
		my_log.add(the_process);
	}
	
	private int getHighestPriority() {
		int result;
		if (!my_list_of_processes.get(0).isEmpty()) {
			result = 0;
		} else if (!my_list_of_processes.get(1).isEmpty()) {
			result = 1;
		} else {
			result = 2;
		}
		
		return result;
	}
	@Override
	public boolean systemCall(Thread the_input) {
		// TODO Auto-generated method stub
		return false;
	}

	
	/**
	 * String representation of this RoundRobinScheduler.
	 */
	@Override
	public String toString() {
		StringBuilder b = new StringBuilder();
		if(!my_list_of_processes.get(0).isEmpty())
		{
			b.append("Scheduler: Running process " + my_list_of_processes.size() +" "+ my_list_of_processes.get(0).get(0).getName() + " next.\n");
		}
		else if(!my_list_of_processes.get(1).isEmpty())
		{
			b.append("Scheduler: Running process " + my_list_of_processes.size() +" "+ my_list_of_processes.get(1).get(0).getName() + " next.\n");
		}
		else 
		{
			b.append("Scheduler: Running process " + my_list_of_processes.size() +" "+ my_list_of_processes.get(2).get(0).getName() + " next.\n");
		}
		
		for(List<Processes> the_list: my_list_of_processes)
		{
		  for (Processes the_process: the_list) {
			if(!the_process.can_run)
			{
				b.append(the_process.getName() + "' state: BLOCKED\n");
			}
			else if(the_process.running)
			{
				b.append(the_process.getName() + "' state: RUNNING\n");
			}
			else
			{
				b.append(the_process.getName() + "' state: RUNNABLE\n");
			}
		  }	
		}
		
		b.append("...............");
		return b.toString();
	}
}
